


#include <rtthread.h>
#include <rtdevice.h>
#include "drv_gpio.h"
#include "drv_clock.h"

#define PWM_CHANNEL_MAX     (8) /* 0-7*/

#define PWM_CHANNEL7        7

#if defined(RT_USING_PWM) && defined(BSP_USING_PWM)

#define DBG_ENABLE
#define DBG_SECTION_NAME  "[PWM]"
#define DBG_LEVEL         LOG_LVL_DBG
#define DBG_COLOR
#include <rtdbg.h>


#define  PWM_BASE           0x02000C00UL

#define PWM_PIER            0x00  /*PWM IRQ enable register 0x00*/
#define PWM_PISR            0x04  /*PWM IRQ status register 0x04*/
#define PWM_CIER            0x10  /*PWM capture IRQ enable register 0x10*/

#define PWM_CISR            0X14  /*PWM capture IRQ status register 0X14*/

#define PWM_PCCR_BASE       0x20
#define PWM_PCCR01          0x20  /*PWM01 clock configuration register*/
#define PWM_PCCR23          0x24  /*PWM23 clock configuration register */
#define PWM_PCCR45          0x28  /*PWM45 clock configuration register */
#define PWM_PCCR67          0x2C  /*PWM67 clock configuration register */
#define PWM_PCCR8           0x30  /*PWM8 clock configuration register */

#define PWM_PCGR            0X40  /*PWM CLOCK Gating Register  0X40*/

#define PWM_PDZCR_BASE      0X60
#define PWM_PDZCR01         0X60  /*PWM01 Dead Zone Contol Register 0X60*/
#define PWM_PDZCR23         0X64  /*PWM23 Dead Zone Contol Register 0X64*/
#define PWM_PDZCR45         0X68  /*PWM45 Dead Zone Contol Register 0X68*/
#define PWM_PDZCR67         0X6C  /*PWM67 Dead Zone Contol Register 0X6C*/
#define PWM_PDZCR89         0X70  /*PWM89 Dead Zone Contol Register 0X70*/

#define PWM_PER             0x80  /*enable register 0x80*/

#define PWM_PGR0            0X90  /*PWM Group0 Register 0X90*/
#define PWM_PGR1            0X94  /*PWM Group0 Register 0X94*/

#define PWM_CER             0xc0  /*PWM Capture Enable Register */

#define PWM_PCR             0x0100/*PWM Contorl Register */
/* offset: 0x0100+0x0000+N*0x0020 N=0~8 */

#define PWM_PPR             0x0104/*PWM Period Register */
/* offset: 0x0100+0x00004+N*0x0020 N=0~8 */

#define PWM_PCNTR           0x0108/*PWM Counter Register */
/* offset: 0x0100+0x0008+N*0x0020 N=0~8 */

#define PWM_PPCNTR          0x010C/*PWM Pulse Counter Register */
/* offset: 0x0100+0x000c+N*0x0020 N=0~8 */

#define PWM_CCR             0x0110/*Capture Control Register */
/* offset: 0x0100+0x0010+N*0x0020 N=0~8 */

#define PWM_CRLR            0x0114/*Capture RIse Lock Register */
/* offset: 0x0100+0x0014+N*0x0020 N=0~8 */

#define PWM_CFLR            0x0118/*Capture Control Register */
/* offset: 0x0100+0x0018+N*0x0020 N=0~8 */

#define PWM_VR              0x03f0/*PWM Version Register */


/*****************************************************************************
 * define  PWM SET
 *****************************************************************************/
#define PWM_CLK_SRC_SHIFT   0x7
#define PWM_CLK_SRC_WIDTH   0x2

#define PWM_DIV_M_SHIFT     0x0
#define PWM_DIV_M_WIDTH     0x4

#define PWM_PRESCAL_SHIFT     0x0
#define PWM_PRESCAL_WIDTH     0x8

#define PWM_ACTIVE_CYCLES_SHIFT     0x0
#define PWM_ACTIVE_CYCLES_WIDTH     0x10

#define PWM_PERIOD_SHIFT     0x10
#define PWM_PERIOD_WIDTH     0x10

#define PWM_CLK_GATING_SHIFT    0x0
#define PWM_CLK_GATING_WIDTH    0x1

#define PWM_EN_CONTROL_SHIFT    0x0
#define PWM_EN_CONTORL_WIDTH    0x1

#define PWM_ACT_STA_SHIFT     0x8
#define PWM_ACT_STA_WIDTH     0x1
#define PWM_NUM 8

#define SUNXI_PWM_CLK_TYPE HAL_SUNXI_CCU
#define SUNXI_PWM_CLK_ID CLK_BUS_PWM
#define SUNXI_PWM_RESET_TYPE HAL_SUNXI_RESET
#define SUNXI_PWM_RESET_ID RST_BUS_PWM

#define SET_REG_VAL(reg_val, shift, width, set_val)     ((reg_val & ~((-1UL) >> (32 - width) << shift)) | (set_val << shift))
#define SETMASK(width, shift)   ((width?((-1U) >> (32-width)):0)  << (shift))
#define CLRMASK(width, shift)   (~(SETMASK(width, shift)))
#define GET_BITS(shift, width, reg)     \
	(((reg) & SETMASK(width, shift)) >> (shift))
#define SET_BITS(shift, width, reg, val) \
	(((reg) & CLRMASK(width, shift)) | (val << (shift)))

/* define shift and width */
#define PWM_CLK_SRC_SHIFT 0x7
#define PWM_CLK_SRC_WIDTH 0x2

#define PWM_DIV_M_SHIFT 0x0
#define PWM_DIV_M_WIDTH 0x4

#define PWM_PRESCAL_SHIFT 0x0
#define PWM_PRESCAL_WIDTH 0x8

#define PWM_ACT_CYCLES_SHIFT 0x0
#define PWM_ACT_CYCLES_WIDTH 0x10

#define PWM_PERIOD_CYCLES_SHIFT 0x10
#define PWM_PERIOD_CYCLES_WIDTH 0x10

#define PRESCALE_MAX 256

#define read32(addr)           (*(volatile unsigned int *)(addr))
#define read8(addr) 			( *(volatile unsigned char *)(addr))
#define write32(addr, value)    (*(volatile unsigned int *)(addr) = (value))
#define write8(addr, value)    (*(volatile unsigned char *)(addr) = (value))

#define pwm_do_div(n,base) ({                   \
        uint32_t __base = (base);                \
        uint32_t __rem;                      \
        __rem = ((uint64_t)(n)) % __base;            \
        (n) = ((uint64_t)(n)) / __base;              \
        if (__rem > __base / 2) \
            ++(n); \
        __rem;                          \
    })

struct rt_hal_pwm
{
    struct rt_device_pwm parent;

    rt_uint32_t period[PWM_CHANNEL_MAX];
    rt_uint32_t pulse[PWM_CHANNEL_MAX];
    rt_uint32_t gpioport[PWM_CHANNEL_MAX];
    rt_uint32_t gpiopin[PWM_CHANNEL_MAX];
    rt_uint32_t gpiofunc[PWM_CHANNEL_MAX];
};

static struct rt_hal_pwm _hal_pwm_device;

struct rt_pwm_configuration pwm_device;

static rt_err_t set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
{
    struct rt_hal_pwm *hal_pwm_device = (struct rt_hal_pwm *)device;


    hal_pwm_device->period[configuration->channel] = configuration->period;
    hal_pwm_device->pulse[configuration->channel] = configuration->pulse;

    return RT_EOK;
}

static rt_err_t get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
{
    struct rt_hal_pwm *hal_pwm_device = (struct rt_hal_pwm *)device;

    configuration->period = hal_pwm_device->period[configuration->channel];
    configuration->pulse = hal_pwm_device->pulse[configuration->channel];
    
    return RT_EOK;
}

static void pwm_pins_config(struct rt_hal_pwm *hal_pwm_device, uint32_t channel_in, enum gpio_port port, enum gpio_pin pin, rt_uint8_t func)
{
    hal_pwm_device->gpioport[channel_in] = port;
    hal_pwm_device->gpiopin[channel_in] = pin;
    hal_pwm_device->gpiofunc[channel_in] = func;
       /* pwm, channel 7 */
}

void hal_pwm_enable_controller(uint32_t channel_in)
{
    unsigned long reg_addr = PWM_BASE + PWM_PER;
    uint32_t reg_val;

    reg_val = read32(reg_addr);
    reg_val |= 1 << channel_in;

    write32(reg_addr, reg_val);
}

void hal_pwm_porality(uint32_t channel_in)
{
    uint32_t reg_val;
    unsigned long reg_addr = PWM_BASE + PWM_PCR;

    uint32_t channel = channel_in;
    reg_addr += 0x20 * channel;
    /*set polarity*/
    reg_val = read32(reg_addr);
    reg_val = SET_REG_VAL(reg_val, PWM_ACT_STA_SHIFT, PWM_ACT_STA_WIDTH, 1);
    write32(reg_addr, reg_val);
}

static uint32_t get_pccr_reg_offset(uint32_t channel)
{
    uint32_t val;

    switch (channel)
    {
        case 0:
        case 1:
            return PWM_PCCR01;
            break;
        case 2:
        case 3:
            return PWM_PCCR23;
            break;
        case 4:
        case 5:
            return PWM_PCCR45;
            break;
        case 6:
        case 7:
            return PWM_PCCR67;
            break;
        default :
            LOG_E("channel is error ");
            return PWM_PCCR01;
            break;
    }
}

static int t113_pwm_control(uint32_t channel_in, struct rt_hal_pwm *config_pwm)
{
    int channel = channel_in;
    // struct rt_hal_pwm *config_pwm = &_hal_pwm_device;
    uint32_t ret;
    unsigned int temp;
    unsigned long long c = 0;
    unsigned long entire_cycles = 256, active_cycles = 192;
    unsigned int reg_offset, reg_shift, reg_width;
    unsigned int reg_bypass_shift /*, group_reg_offset*/;
    unsigned int reg_clk_src_shift, reg_clk_src_width;
    unsigned int reg_div_m_shift, reg_div_m_width, value;

    gpio_set_func(config_pwm->gpioport[channel], config_pwm->gpiopin[channel], config_pwm->gpiofunc[channel]);

    hal_pwm_enable_controller(channel);

    /* pwm set polarity */
    hal_pwm_porality(channel);


    /* pwm config function */
    uint32_t pre_scal_id = 0, div_m = 0, prescale = 0;
    uint32_t pre_scal[][2] =
    {
        /*reg_val   clk_pre_div*/
        {0, 1},
        {1, 2},
        {2, 4},
        {3, 8},
        {4, 16},
        {5, 32},
        {6, 64},
        {7, 128},
        {8, 256},
    };

    reg_clk_src_shift = PWM_CLK_SRC_SHIFT;
    reg_clk_src_width = PWM_CLK_SRC_WIDTH;
    reg_offset = get_pccr_reg_offset(channel);

    

    if (config_pwm->period[channel] > 0 && config_pwm->period[channel] <= 10)
    {
        /* if freq lt 100M, then direct output 100M clock,set by pass. */
        c = 100000000;
        reg_bypass_shift = channel;
        reg_offset = get_pccr_reg_offset(channel);

        temp = read32(PWM_BASE + PWM_PCGR);
        temp = SET_BITS(reg_bypass_shift, 1, temp, 1); /* bypass set */
        write32(PWM_BASE + PWM_PCGR, temp);
        /*clk_src_reg*/
        temp = read32(PWM_BASE + reg_offset);
        temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);/*clock source*/
        write32(PWM_BASE + reg_offset, temp);

        return 0;
    }
    else if (config_pwm->period[channel] > 10 && config_pwm->period[channel] <= 334)
    {
        /* if freq between 3M~100M, then select 100M as clock */
        c = 100000000;

        /*clk_src_reg : use APB1 clock */
        temp = read32(PWM_BASE + reg_offset);
        temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);
        write32(PWM_BASE + reg_offset, temp);
    }
    else if (config_pwm->period[channel] > 334)
    {
        /* if freq < 3M, then select 24M clock */
        c = 24000000;

        /*clk_src_reg : use OSC24M clock */
        temp = read32(PWM_BASE + reg_offset);
        temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 0);
        write32(PWM_BASE + reg_offset, temp);
    }
    
    c = c * config_pwm->period[channel];
    pwm_do_div(c, 1000000000);
    entire_cycles = (unsigned long)c;

    for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++)
    {
        if (entire_cycles <= 65536)
        {
            break;
        }
        for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++)
        {
            entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1);
            if (entire_cycles <= 65536)
            {
                div_m = pre_scal[pre_scal_id][0];
                break;
            }
        }
    }
    
    c = (unsigned long long)entire_cycles * config_pwm->pulse[channel];
    pwm_do_div(c, config_pwm->period[channel]);
    active_cycles = c;
    if (entire_cycles == 0)
    {
        entire_cycles++;
    }

    /* config clk div_m */
    reg_div_m_shift = PWM_DIV_M_SHIFT;
    reg_div_m_width = PWM_DIV_M_WIDTH;
    temp = read32(PWM_BASE + reg_offset);
    temp = SET_BITS(reg_div_m_shift, reg_div_m_width, temp, div_m);
    write32(PWM_BASE + reg_offset, temp);

    /* config gating */
    reg_shift = channel;
    value = read32(PWM_BASE + PWM_PCGR);
    value = SET_BITS(reg_shift, 1, value, 1);/* set gating */
    write32(PWM_BASE + PWM_PCGR, value);
   
    /* config prescal */
    reg_offset = PWM_PCR + 0x20 * channel;
    reg_shift = PWM_PRESCAL_SHIFT;
    reg_width = PWM_PRESCAL_WIDTH;
    temp = read32(PWM_BASE + reg_offset);
    temp = SET_BITS(reg_shift, reg_width, temp, prescale);
    write32(PWM_BASE + reg_offset, temp);

    /* config active cycles */
    reg_offset = PWM_PPR + 0x20 * channel;
    reg_shift = PWM_ACT_CYCLES_SHIFT;
    reg_width = PWM_ACT_CYCLES_WIDTH;
    temp = read32(PWM_BASE + reg_offset);
    temp = SET_BITS(reg_shift, reg_width, temp, active_cycles);
    write32(PWM_BASE + reg_offset, temp);

    /* config period cycles */
    reg_offset = PWM_PPR + 0x20 * channel;
    reg_shift = PWM_PERIOD_CYCLES_SHIFT;
    reg_width = PWM_PERIOD_CYCLES_WIDTH;
    temp = read32(PWM_BASE + reg_offset);
    temp = SET_BITS(reg_shift, reg_width, temp, (entire_cycles - 1));
    write32(PWM_BASE + reg_offset, temp);

    return 0;
}

static void t113_pwm_reset_deassert(void)
{
    uint32_t add = T113_CCU_BASE+CCU_PWM_BGR_REG;
    uint32_t reg32 = read32(add);
    uint32_t channel = 6, reg_shift = 6, value;

    reg32 |= 1<<0;
    write32(add, reg32);
    reg32 = read32(add);
    reg32 |= 1<<16;
    write32(add, reg32);

    rt_kprintf("deassert %p\n", read32(add));
}

static rt_err_t control(struct rt_device_pwm *device, int cmd, void *arg)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration * configuration = (struct rt_pwm_configuration *)arg;

    struct rt_hal_pwm *hal_pwm_device = (struct rt_hal_pwm *)device;

    LOG_D("drv_pwm.c control cmd: %d. \n", cmd);

    if (configuration->channel > (PWM_CHANNEL_MAX - 1))
    {
        LOG_E("drv_pwm.c control channel: %d not support! \n", configuration->channel);
        return -RT_EIO;
    }

    LOG_D("PWM: channel:%d", configuration->channel);

    if (cmd == PWM_CMD_ENABLE)
    {
        LOG_D("PWM_CMD_ENABLE");
        if (t113_pwm_control(configuration->channel, hal_pwm_device) != 0)
        {
            result = -RT_EIO;
        }
    }
    else if (cmd == PWM_CMD_DISABLE)
    {
        LOG_D("PWM_CMD_DISABLE");
        if (t113_pwm_control(configuration->channel, hal_pwm_device) != 0)
        {
            result = -RT_EIO;
        }
    }
    else if (cmd == PWM_CMD_SET)
    {
        LOG_D("PWM_CMD_SET");
        result = set(device, (struct rt_pwm_configuration *)arg);
    }
    else if (cmd == PWM_CMD_GET)
    {
        LOG_D("PWM_CMD_GET");
        result = get(device, (struct rt_pwm_configuration *)arg);
    }

    return result;
}

static const struct rt_pwm_ops pwm_ops =
{
    control,
};



int rt_hw_pwm_init(void)
{

    pwm_pins_config(&_hal_pwm_device, PWM_CHANNEL7, GPIO_PORT_D, GPIO_PIN_22, IO_FUN_5);
    t113_pwm_reset_deassert();

    return rt_device_pwm_register(&_hal_pwm_device.parent, "pwm", &pwm_ops, RT_NULL);
}
INIT_DEVICE_EXPORT(rt_hw_pwm_init);



#endif /* RT_USING_PWM && BSP_USING_PWM */

